package com.ecarx.vhaltests.hy11.testcases.drivemode;

import static android.car.hardware.CarPropertyValue.STATUS_UNAVAILABLE;
import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DRIVE_MODE_SELECTION_DYNAMIC;
import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DRIVE_MODE_SELECTION_NORMAL;
import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DRIVE_MODE_SELECTION_OFFROAD;

import android.car.VehicleAreaType;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyManager;

import androidx.test.platform.app.InstrumentationRegistry;

import com.ecarx.vhaltests.IpcpClient;
import com.ecarx.vhaltests.VehicleConstants;
import com.ecarx.vhaltests.VhalUnitTestBase;
import com.ecarx.xui.adaptapi.car.IWrapper;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DriveModeSelectionOffroadTest extends VhalUnitTestBase {
    private static int propertyId;
    private static IWrapper.IPropertyId wrapperProperty;
    private static IpcpClient ipcpClient = new IpcpClient();
    ;
    private boolean isFirstCbk;
    private static int AREA_ID = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
    private CarPropertyManager.CarPropertyEventCallback propertyCbk;

    private int cbkStatus;
    private final Object cbkLock = new Object();

    @BeforeClass
    public static void testInit() {
        IWrapper wrapper = com.ecarx.xui.adaptapi.car.Car.createWrapper(InstrumentationRegistry.getInstrumentation().getTargetContext());
        wrapperProperty = wrapper.getWrappedPropertyId(IWrapper.WrappedIdType.ID_TYPE_FUNCTION, DRIVE_MODE_SELECTION_OFFROAD);
        propertyId = wrapperProperty.getPropertyId();
    }

    @Test
    public void test001_SetDriveModeSelectionOffroadAvail() {
        setAvailable(true);
        delay();
        Assert.assertTrue(isAvail());
    }

    @Test
    public void test002_SetDriveModeSelectionOffroadUnAvail() {
        setAvailable(false);
        Assert.assertFalse(isAvail());
    }

    @Test
    public void test003_SetDriveModeSelectionOffroadCbk() {
        cbkStatus = -1;
        setAvailable(true);
        delay();
        Assert.assertTrue(isAvail());
        delay();
        propertyCbk = new CarPropertyManager.CarPropertyEventCallback() {
            @Override
            public void onChangeEvent(CarPropertyValue carPropertyValue) {
                if (carPropertyValue.getAreaId() == AREA_ID) {
                    if (isFirstCbk) {
                        isFirstCbk = false;
                    } else {
                        cbkStatus = carPropertyValue.getStatus();
                        unLock();
                    }
                }
            }

            @Override
            public void onErrorEvent(int i, int i1) {
            }
        };
        mCarPropertyMgr.registerCallback(propertyCbk, propertyId, CarPropertyManager.SENSOR_RATE_ONCHANGE);
        delay();
        setAvailable(false);
        lock();
        Assert.assertEquals(STATUS_UNAVAILABLE, cbkStatus);
    }

    @Test
    public void test004_SetDriveModeSelectionOffroadLoseAct() {
        // 失活
        setAvailable(true);
        Assert.assertTrue(isAvail());
        setDmFuncDriveModeSelectToOther(DRIVE_MODE_SELECTION_DYNAMIC);
        setAvailable(true, true);
        Assert.assertFalse(isAvail());

        // 失活切换默认模式
        setAvailable(true);
        delay();
        setDmFuncDriveModeSelectToOther(DRIVE_MODE_SELECTION_OFFROAD);
        setAvailable(false);
        delay();
        Assert.assertEquals(DRIVE_MODE_SELECTION_NORMAL, getCurDriveMode());

        // 当只有车速满足失活条件时，5秒后还是失活
        setAvailable(true);
        delay();
        setDmFuncDriveModeSelectToOther(DRIVE_MODE_SELECTION_OFFROAD);
        setAvailable(true, false, true);
        delay();
        Assert.assertEquals(DRIVE_MODE_SELECTION_OFFROAD, getCurDriveMode());
        delay(5000);
        Assert.assertEquals(DRIVE_MODE_SELECTION_NORMAL, getCurDriveMode());

        // 当只有车速满足失活条件时，5秒内又不满足失活条件
        setAvailable(true);
        delay();
        setDmFuncDriveModeSelectToOther(DRIVE_MODE_SELECTION_OFFROAD);
        setAvailable(true, false, true);
        delay();
        Assert.assertEquals(DRIVE_MODE_SELECTION_OFFROAD, getCurDriveMode());
        delay();
        setAvailable(true);
        delay(5000);
        Assert.assertEquals(DRIVE_MODE_SELECTION_OFFROAD, getCurDriveMode());

    }

    @Test
    public void test005_SetDriveModeSelectionOffroadSteerErrReq() {
        setAvailable(true);
        mockSteerErrReq(1);
        delay();
        Assert.assertTrue(isAvail());
    }


    private static void setAvailable(boolean isAvail) {
        setAvailable(isAvail, false);
    }

    private static void setAvailable(boolean isAvail, boolean isTestLoseAct) {
        setAvailable(isAvail, isTestLoseAct, false);
    }

    private static void setAvailable(boolean isAvail, boolean isTestLoseAct, boolean isTestDelay) {
        int cemSvcId = 37;
        int[] cemOperIds = {999};

        int ccSvcId = 135;
        int[] ccOperIds = {200};
        int[] configs = {13, 604};
        int[] values = {4, 2};

        int vddmSvcId = 48;
        int[] vddmOperIds = {999};

        int asdmSvcId = 31;
        int[] asdmOperIds = {999};

        int timeoutSvcId = 99;
        int[] timeoutOperIds = {200};

        ipcpClient.setConfig(cemSvcId, cemOperIds);
        ipcpClient.setConfig(ccSvcId, ccOperIds);
        ipcpClient.setConfig(vddmSvcId, vddmOperIds);
        ipcpClient.setConfig(asdmSvcId, asdmOperIds);
        ipcpClient.setConfig(timeoutSvcId, timeoutOperIds);

        String[] cems = {"vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts"};
        int[] cemValues = {VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_CONVENIENCE};
        boolean cemRet = ipcpClient.setCEMSignal(cemSvcId, cemOperIds[0], cems, cemValues);
        Assert.assertTrue(cemRet);

        String[] vddms = {"escStEscSt", "steerErrReq", "prpsnModOffroadBlkd", "crsCtrlrSts", "adjSpdLimnSts", "escCtrlIndcn", "vehSpdLgtA"};
        int[] vddmValues = {isAvail ? 1 : 3, 0, 0, 2, 2, isTestLoseAct ? 0 : 1, isTestDelay ? (int)(100f/0.00391f) : 0};
        boolean vddmRet = ipcpClient.setVDDMSignal(vddmSvcId, vddmOperIds[0], vddms, vddmValues);
        Assert.assertTrue(vddmRet);
        //降频速度模拟间隔要超1秒
        delay(1000);

        String[] asdm = {"asyALgtIndcr", "asyALatIndcr", "asyHandsOffIndcrAsyHandsOffIndcr"};
        int[] asdmValues = {2, 2, 2};
        boolean asdmResult = ipcpClient.setASDMSignal(asdmSvcId, asdmOperIds[0], asdm, asdmValues);
        Assert.assertTrue(asdmResult);

        String[] timeoutConfigs = {"steerErrReqTimeout"};
        int[] timeoutValues = {0};
        boolean toRet = ipcpClient.setTimeoutSignal(timeoutSvcId, timeoutOperIds[0], timeoutConfigs, timeoutValues);
        Assert.assertTrue(toRet);

        boolean ccRet = ipcpClient.setCarconfigSignal(ccSvcId, ccOperIds[0], configs, values);
        Assert.assertTrue(ccRet);
    }

    private static void mockSteerErrReq(int value) {
        int vddmSvcId = 48;
        int[] vddmOperIds = {999};
        ipcpClient.setConfig(vddmSvcId, vddmOperIds);
        String[] vddms = {"escStEscSt", "steerErrReq", "prpsnModOffroadBlkd", "crsCtrlrSts", "adjSpdLimnSts", "escCtrlIndcn", "vehSpdLgtA"};
        int[] vddmValues = {1, value, 0, 2, 2, 1, 0};
        boolean vddmRet = ipcpClient.setVDDMSignal(vddmSvcId, vddmOperIds[0], vddms, vddmValues);
        Assert.assertTrue(vddmRet);
    }

    private static boolean isAvail() {
        delay();
        return mCarPropertyMgr.isPropertyAvailable(propertyId, AREA_ID);
    }

    private void lock() {
        try {
            synchronized (cbkLock) {
                cbkLock.wait(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void unLock() {
        synchronized (cbkLock) {
            cbkLock.notify();
        }
    }

}